﻿using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Workbench.Models;
using Workbench.Shared;

namespace Workbench.Presentation
{
    public class BaseFormViewModel : ViewModelBase
    {
        public BaseFormViewModel(BaseFormModel dataModel)
        {
            DataModel = dataModel;
            Content = dataModel.Content;

            dataModel.PropertyChanged += dataModel_PropertyChanged;
        }

        void dataModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "Content":
                    this.Content = DataModel.Content;
                    break;
            }
        }

        public BaseFormModel DataModel { get; private set; }

        private string content;
        public string Content
        {
            get { return content; }
            set { content = value; RaisePropertyChanged(); }
        }

        private bool isSelected;
        public bool IsSelected
        {
            get { return isSelected; }
            set { isSelected = value; RaisePropertyChanged(); }
        }
    }

    public abstract class WordViewModelBase : ViewModelBase
    {
        [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public WordViewModelBase(WordModel dataModel)
        {
            DataModel = dataModel;
            Content = DataModel.GetContent();
            if (dataModel.BaseForm != null)
            {
                BaseForm = dataModel.BaseForm.Content;
            }
            IsSet = CalcIsSet();
            UniqueWordId = dataModel.UniqueWordId;

            dataModel.PropertyChanged += dataModel_PropertyChanged;
        }

        void dataModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "BaseForm":
                    if (DataModel.BaseForm != null) BaseForm = DataModel.BaseForm.Content;
                    else BaseForm = null;
                    break;
            }
            IsSet = CalcIsSet();
        }

        public WordModel DataModel { get; private set; }

        private string content;
        public string Content
        {
            get { return content; }
            set { content = value; RaisePropertyChanged(); }
        }

        private bool isSelected;
        public bool IsSelected
        {
            get { return isSelected; }
            set { isSelected = value; RaisePropertyChanged(); }
        }

        private bool isSet;
        public bool IsSet
        {
            get { return isSet; }
            set { isSet = value; RaisePropertyChanged(); }
        }

        private string baseForm;
        public string BaseForm
        {
            get
            {
                return baseForm;
            }
            set
            {
                baseForm = value;
                RaisePropertyChanged();
            }
        }

        public long UniqueWordId;

        protected abstract bool CalcIsSet();
    }

    public class LemmatizerWordViewModel : WordViewModelBase
    {
        public LemmatizerWordViewModel(WordModel dataModel)
            : base(dataModel)
        {

        }

        protected override bool CalcIsSet()
        {
            return this.BaseForm != null;
        }
    }

    public class POSTaggerWordViewModel : WordViewModelBase
    {
        public POSTaggerWordViewModel(WordModel dataModel)
            : base(dataModel)
        {
            dataModel.POSTypes.CollectionChanged += POSTypes_CollectionChanged;
        }

        void POSTypes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            IsSet = CalcIsSet();
        }

        protected override bool CalcIsSet()
        {
            return DataModel.POSTypes.Count != 0;
        }
    }

    [DebuggerDisplay("TreeNodeVM: Name={Name}, IsSelected={IsSelected}")]
    public class ProjectItemViewModel : ViewModelBase
    {
        public long RowId;

        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; RaisePropertyChanged(); }
        }

        protected bool isSelected;
        public bool IsSelected
        {
            get { return isSelected; }
            set { isSelected = value; RaisePropertyChanged(); }
        }

        protected bool isExpanded;
        public bool IsExpanded
        {
            get { return isExpanded; }
            set { isExpanded = value; RaisePropertyChanged(); }
        }

        private ProjectDirectoryViewModel parent;
        public ProjectDirectoryViewModel Parent
        {
            get { return parent; }
            set { parent = value; RaisePropertyChanged(); }
        }
    }

    [DebuggerDisplay("DirectoryVM: Name={Name}, IsSelected={IsSelected}, Children={Children.Count}")]
    public class ProjectDirectoryViewModel : ProjectItemViewModel
    {
        public ProjectDirectoryViewModel(ProjectDirectoryModel dataModel)
        {
            RowId = dataModel.RowId;
            Name = dataModel.Name;

            children.CollectionChanged += children_CollectionChanged;
            dataModel.Children.ForEach(addChild);
        }

        #region Event Handlers

        private void addChild(ProjectItemModel child)
        {
            ProjectItemViewModel childItem = null;
            var childFile = child as ProjectFileModel;
            if (childFile != null)
            {
                childItem = new ProjectFileViewModel(childFile);
            }
            else
            {
                childItem = new ProjectDirectoryViewModel(child as ProjectDirectoryModel);
            }
            Children.Add(childItem);
        }

        private void children_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (var treeNode in e.NewItems.Cast<ProjectItemViewModel>())
                {
                    Debug.Assert(treeNode.Parent == null, "Nodes should not be reused");
                    treeNode.Parent = this;
                }
            }
            if (e.OldItems != null)
            {
                foreach (var treeNode in e.OldItems.Cast<ProjectItemViewModel>())
                {
                    treeNode.Parent = null;
                }
            }
        }

        #endregion Event Handlers

        private ObservableCollection<ProjectItemViewModel> children = new ObservableCollection<ProjectItemViewModel>();
        public ObservableCollection<ProjectItemViewModel> Children
        {
            get { return children; }
        }

        public void SetPropertyChangedHandlerRecursive(PropertyChangedEventHandler handler)
        {
            PropertyChanged -= handler;
            PropertyChanged += handler;

            foreach (var child in Children)
            {
                var childDirectoryViewModel = child as ProjectDirectoryViewModel;
                if (childDirectoryViewModel != null)
                {
                    childDirectoryViewModel.SetPropertyChangedHandlerRecursive(handler);
                }
                else
                {
                    child.PropertyChanged -= handler;
                    child.PropertyChanged += handler;
                }
            }
        }
    }

    [DebuggerDisplay("FileVM: Name={Name}, IsSelected={IsSelected}, ContextIncluded={ContextIncluded}")]
    public class ProjectFileViewModel : ProjectItemViewModel
    {
        public ProjectFileViewModel(ProjectFileModel dataModel)
        {
            Name = dataModel.Name;
            RowId = dataModel.RowId;
            ContextIncluded = dataModel.ContextIncluded;
        }

        private bool contextIncluded;
        public bool ContextIncluded
        {
            get { return contextIncluded; }
            set { contextIncluded = value; RaisePropertyChanged(); }
        }
    }

    [DebuggerDisplay("POSType: Name={Name}, Parent={Parent.Name}, Children={Children.Count}, IsSelected={IsSelected}, IsExpanded={IsExpanded}")]
    public class POSTypeViewModel : ViewModelBase
    {
        public POSTypeViewModel(POSTypeModel dataModel)
        {
            Name = dataModel.Name;
            Count = dataModel.Count;
            DataModel = dataModel;
            DataModel.PropertyChanged += dataModel_PropertyChanged;
            DataModel.Children.CollectionChanged += dataModel_Children_CollectionChanged;
            Children.CollectionChanged += children_CollectionChanged;
            Children.AddRange(DataModel.Children.Select(x => new POSTypeViewModel(x)));
        }
        public POSTypeModel DataModel;

        void dataModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Name")
                this.Name = DataModel.Name;
            else if (e.PropertyName == "Count")
                this.Count = DataModel.Count;
        }

        private void dataModel_Children_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (var child in e.NewItems.Cast<POSTypeModel>())
                {
                    Children.Add(new POSTypeViewModel(child));
                }
            }
            if (e.OldItems != null)
            {
                foreach (var child in e.OldItems.Cast<POSTypeModel>())
                {
                    int i = 0;
                    for (; i < Children.Count; i++)
                    {
                        if (Children[i].DataModel.Equals(child))
                        {
                            break;
                        }
                    }
                    Children.RemoveAt(i);
                }
            }
        }

        private void children_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (var treeNode in e.NewItems.Cast<POSTypeViewModel>())
                {
                    Debug.Assert(treeNode.Parent == null, "Nodes should not be reused");
                    treeNode.Parent = this;
                }
            }
            if (e.OldItems != null)
            {
                foreach (var treeNode in e.OldItems.Cast<POSTypeViewModel>())
                {
                    treeNode.Parent = null;
                }
            }
        }

        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; RaisePropertyChanged();}
        }

        private bool isSelected;
        public bool IsSelected
        {
            get { return isSelected; }
            set { isSelected = value; RaisePropertyChanged(); }
        }

        private bool isExpanded;
        public bool IsExpanded
        {
            get { return isExpanded; }
            set { isExpanded = value; RaisePropertyChanged(); }
        }

        private long count;
        public long Count
        {
            get { return count; }
            set { count = value; RaisePropertyChanged(); }
        }

        private POSTypeViewModel parent;
        public POSTypeViewModel Parent
        {
            get { return parent; }
            set { parent = value; RaisePropertyChanged(); }
        }

        private ObservableCollection<POSTypeViewModel> children = new ObservableCollection<POSTypeViewModel>();
        public ObservableCollection<POSTypeViewModel> Children
        {
            get { return children; }
        }

        public void SetPropertyChangedHandlerRecursive(PropertyChangedEventHandler handler)
        {
            PropertyChanged -= handler;
            PropertyChanged += handler;

            foreach (var child in Children)
            {
                child.SetPropertyChangedHandlerRecursive(handler);
                child.PropertyChanged -= handler;
                child.PropertyChanged += handler;
            }
        }
    }

    [DebuggerDisplay("POSType: Name={Name}, Level={Level}")]
    public class FlattenPOSTypeViewModel : ViewModelBase
    {
        public FlattenPOSTypeViewModel(POSTypeModel dataModel, int level)
        {
            Name = dataModel.Name;
            Level = level;
            DataModel = dataModel;
            DataModel.PropertyChanged += dataModel_PropertyChanged;
            IsEnabled = true;
        }
        public POSTypeModel DataModel;

        void dataModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Name")
                this.name = DataModel.Name;
        }

        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; RaisePropertyChanged(); }
        }

        private int level;
        public int Level
        {
            get { return level; }
            set { level = value; RaisePropertyChanged(); }
        }

        private bool isEnabled;
        public bool IsEnabled
        {
            get { return isEnabled; }
            set { isEnabled = value; RaisePropertyChanged(); }
        }
    }

    public class ExerciseSuiteViewModel : ViewModelBase
    {
        public ExerciseSuiteViewModel(ExerciseSuiteModel dataModel)
        {
            this.DataModel = dataModel;
            this.name = dataModel.Name;
        }

        public ExerciseSuiteModel DataModel;

        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; RaisePropertyChanged(); }
        }

        private bool isSelected;
        public bool IsSelected
        {
            get { return isSelected; }
            set { isSelected = value; RaisePropertyChanged(); }
        }
    }

    public class ConcordancerExerciseViewModel : ViewModelBase
    {
        public ConcordancerExerciseViewModel(ConcordancerExerciseModel dataModel)
        {
            this.DataModel = dataModel;
            this.name = dataModel.Name;
            this.wordContent = dataModel.Word.Content;
        }

        public ConcordancerExerciseModel DataModel;

        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; RaisePropertyChanged(); }
        }

        private string sentencesPlain;

        public string SentencesPlain
        {
            get { return sentencesPlain; }
            set { sentencesPlain = value; RaisePropertyChanged(); }
        }

        private string wordContent;
        public string WordContent
        {
            get { return wordContent; }
            set { wordContent = value; RaisePropertyChanged(); }
        }

        private bool isSelected;
        public bool IsSelected
        {
            get { return isSelected; }
            set { isSelected = value; RaisePropertyChanged(); }
        }
    }
}
